import mutagen
import os
import Image
from StringIO import StringIO

def MusicFile(file):
    tagged = MusicFileTags(file)
    if tagged.parse_ok:
        return tagged
    
    return

class MusicFileTags():
    
    artist      = [""]
    album       = [""]
    title       = [""]
    tracknumber = [""]
    parse_ok    = False
    
    available_tags = ["artist", "album", "title", "tracknumber"]
    
    _file       = None
    _raw        = None
    _raw_easy   = None
    _picture    = None
    
    def __init__(self, file):
        if os.path.isfile(file):
            self._file = file
        else:
            ## this shouldn't happen
            return
        
        self._raw = mutagen.File(file)
        if not self._raw:
            ## the given file is not supported by mutagen
            return
        
        self.parseMetadata()
    
    
    def __getitem__(self, key):
        try:
            return self.getTag(key)
        except AttributeError:
            raise TagNotSupportedError(key)
    
    def parseMetadata(self):
        
        ## file is in FLAC format
        if isinstance(self._raw, mutagen.flac.FLAC):

            for tag in self.available_tags:
                setattr(self, tag, self._raw[tag])
                
            if self._raw.pictures:
                self._picture = Image.open(StringIO(self._raw.pictures[0].data))
            else:
                self._picture = None
            
            self.parse_ok = True
        
        ## file is in MP3 format
        if isinstance(self._raw, mutagen.mp3.MP3):
            
            from mutagen.id3 import error
            
            try:
                self._raw.add_tags()
            except error:
                pass
            
            
            ## get picture
            ## type 3 (front cover) is prefered, when not available, the first
            ## picture (lowest id) will be taken
            picturetags = []
            for tag in self._raw.keys():
                if "APIC" in tag:
                    if self._raw[tag].type == 3:
                        self._picture = Image.open(StringIO(self._raw[tag].data))
                        break
                    else:
                        picturetags.append(self._raw[tag])
            
            if (not self._picture) and picturetags:
                self._picture = Image.open(StringIO(picturetags[0].data))
            
            ## use easy id3 tag to read id3 tags
            from mutagen.easyid3 import EasyID3
            from mutagen.mp3 import MP3
            
            ## also load file with EasyID3 to simplify tag handling
            self._raw_easy = MP3(self._file, ID3=EasyID3)
            
            for tag in self.available_tags:
                try:
                    setattr(self, tag, self._raw_easy[tag])
                except KeyError:
                    pass
            
            self.parse_ok       = True
    
    
    def setTag(self, tag, value):
        if not tag in self.available_tags:
            raise TagNotSupportedError(tag)
        
        if not isinstance(value, list):
            value = [value]
        
        setattr(self, tag, value)
        
        if isinstance(self._raw, mutagen.flac.FLAC):
            self._raw[tag] = value
            self._raw.save()
        elif isinstance(self._raw, mutagen.mp3.MP3):
            self._raw_easy[tag] = value
            self._raw_easy.save()
    
    def getTag(self, tag):
        if not tag in self.available_tags:
            raise TagNotSupportedError(tag)
        return getattr(self, tag)[0]
    
    def getPicture(self):
        return self._picture
    
    def setPicture(self, picture):
        self._picture = picture

        ## given file is in FLAC format
        if isinstance(self._raw, mutagen.flac.FLAC):
            if not self._raw.pictures:
                from mutagen.flac import Picture
                self._raw.add_picture(Picture())
            self._raw.pictures[0].data = picture.tostring("jpeg", "RGB")
            self._raw.pictures[0].mime = "image/jpeg"
            self._raw.pictures[0].type = 3
            self._raw.pictures[0].desc = "Front Cover"
            self._raw.pictures[0].width = picture.size[0]
            self._raw.pictures[0].height = picture.size[1]
            self._raw.pictures[0].colors = 0
            self._raw.pictures[0].depth = 8
            self._raw.pictures[0].write()
            self._raw.save()
        
        ## given file is in MP3 format
        if isinstance(self._raw, mutagen.mp3.MP3):
            
            ## delete all included pictures
            for tag in self._raw.keys():
                if "APIC" in tag:
                    del self._raw[tag]
            
            from mutagen.id3 import APIC
            
            ## create new APIC tag
            apic = APIC(
                encoding    = 3,             # utf8
                mime        = "image/jpeg",
                type        = 3,             # front cover
                desc        = u"Front Cover",
                data        = self._picture.tostring("jpeg", "RGB")
            )
            
            self._raw.tags.add(apic)
            self._raw.save()


class TagNotSupportedError(Exception):
    def __init__(self, value):
        self.value = value
    
    def __str__(self):
        return "the tag '%s' is not yet supported" % (self.value)